﻿using System;
using System.ComponentModel;

namespace Zive.Color {

    public enum LabChannels { L, A, B }

    /// <summary>
    /// An implementation of the CIE 1976 L*a*b* colour space.
    /// </summary>
    public class LabColor: INotifyPropertyChanged {

        public LabColor( ) { }

        public LabColor( Illuminants illuminant ) {
            _illuminant = illuminant;
        }

        public LabColor( double L, double a, double b ) {
            _lstar = L;
            _astar = a;
            _bstar = b;
        }

        public LabColor( Illuminants illuminant, double L, double a, double b ) {
            _illuminant = illuminant;
            _lstar = L;
            _astar = a;
            _bstar = b;
        }

        public LabColor( LabColor other ) {
            _illuminant = other._illuminant;
            _lstar = other._lstar;
            _astar = other._astar;
            _bstar = other._bstar;
        }

        public LabColor Clone( ) {
            return new LabColor( this );
        }

        private Illuminants _illuminant = Illuminants.D65;
        /// <summary>Gets or sets the CIE standard illuminant and thus the white point.</summary>
        public Illuminants Illuminant {
            get { return _illuminant; }
            set {
                _illuminant = value;
                _OnPropertyChanged( "Illuminant" );
            }
        }

        private double _lstar;
        /// <summary>Gets or sets the value of the CIELAB L* channel of the color.</summary>
        public double Lstar {
            get { return _lstar; }
            set {
                _lstar = value;
                _OnPropertyChanged( "Lstar" );
            }
        }

        private double _astar;
        /// <summary>Gets or sets the value of the CIELAB a* channel of the color.</summary>
        public double Astar {
            get { return _astar; }
            set {
                _astar = value;
                _OnPropertyChanged( "Astar" );
            }
        }

        private double _bstar;
        /// <summary>Gets or sets the value of the CIELAB b* channel of the color.</summary>
        public double Bstar {
            get { return _bstar; }
            set {
                _bstar = value;
                _OnPropertyChanged( "Bstar" );
            }
        }

        public double this[ LabChannels channel ] {
            get { return _getChannelFuncs[ (int) channel ]( this ); }
            set { _setChannelFuncs[ (int) channel ]( this, value ); }
        }

        private static readonly Func<LabColor, double>[] _getChannelFuncs = {
            color => color._lstar,
            color => color._astar,
            color => color._bstar,
        };

        private static readonly Action<LabColor, double>[ ] _setChannelFuncs = {
            ( color, value ) => { color.Lstar = value; },
            ( color, value ) => { color.Astar = value; },
            ( color, value ) => { color.Bstar = value; },
        };

        #region Object Members

        public override string ToString( ) {
            return string.Format( "L*={0:N1} a*={1:N1} b*={2:N1} illuminant={3:G}", _lstar, _astar, _bstar, _illuminant );
        }

        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        private void _OnPropertyChanged( string propertyName ) {
            var handlers = PropertyChanged;
            if ( null != handlers ) {
                handlers( this, new PropertyChangedEventArgs( propertyName ) );
            }
        }

        #endregion

    }

}
